using System;
using System.Collections;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using System.Reflection;
using System.Collections.Generic;

namespace DbView
{
	/*
	The M of MVP. Reponsibilities:
	1. Knows about persisted config eg connections, views.
	
	
	*/
	/*
		New for 2012: Change log
		Applying resource pattern
	*/
	public class Model : DbViewDriver.IConnectionConfig
	{
		const string TEMPLATE_FILE = @"DbViewTemplates4.txt";

		// Hid the real location of the resource from the presenter layer. Why?
		public DbView.Resources.ConnectionListMgr ConnectionList { get { return DbView.Resources.ConnectionListMgr.Instance; } }

		// public access to model state
		public string CurrentConnection { get { return this.m_CurrentConnectionTag; } }
		public string CurrentDriver { get { return this.ConnectionList.CurrentDriverName; } }
		public DbViewDriver.IDataSource DataSource { get { return this.m_Source; } }

		// Expose the template file to the presenter
		// TODO: bring this into the model.
		public string TemplateFile { get { return FigureOutConfigPath() + @"\" + TEMPLATE_FILE; } }

		public Model() { } // members initialsed where they are declared.

		///////////////////////////////////////////////////////////////
		// IConnectionConfig
		// 
		// Interface for maintaining a list of connection info that a plugin
		// driver can code to. Assumes that the driver code can utilise the 
		// DbView.connInfo class.
		// 

		// get the current list
		public List<DbView.ConnectionInfo> GetConnections()
		{
			List<DbView.ConnectionInfo> connectionList = new List<ConnectionInfo>();
			foreach (ConnectionInfo ci in ConnectionList)
				connectionList.Add(ci);
			return connectionList;
		}

		// return the current connection - may be null
		public DbView.ConnectionInfo GetCurrentConnection()
		{
			if (CurrentConnection.Length == 0) return null;
			// Deal with object state synching.
			// The Datasource will be the master object. The connection list goes stale.
			if (DataSource.DriverName != ConnectionList.CurrentDriverName)
				ConnectionList.CurrentDriverName = this.DataSource.DriverName;
			return ConnectionList[CurrentConnection];
		}

		// return the last saved connection - may be null
		public DbView.ConnectionInfo GetLastUsedConnection()
		{
			return DbView.Resources.ConnectionListMgr.Instance.GetLastUsedConnection();
		}

		// set the current connection
		public void SetCurrentConnection(DbView.ConnectionInfo ci)
		{
			this.SetConnection(ci);
		}

		// save the changes
		public void Save(List<DbView.ConnectionInfo> connectionList)
		{
			ConnectionList.UpdateConnectionList(connectionList.ToArray());
			//ConnectionList.Save();
			SaveConfig(false);
		}

		// IConnectionConfig
		///////////////////////////////////////////////////////////////

		// Hm: what if connection fails. Need to clear the grid.
		public void SetDriver(DbViewDriver.IDataSource driver)
		{
			if (this.m_Source != driver)
			{
				this.m_Source = driver;
				this.m_CurrentConnectionTag = ""; // clear the current connection when you reset the driver.
				// refresh menu help
				// test the concept
				Commands.ResetHelp(CommandHelpRepository.Instance);
			}
		}

		public void SetConnection(ConnectionInfo ci)
		{
			// don't know the source yet
			this.m_Source = null;
			// fetch drivers to search through
			List<Object> drivers = PluginManager.Instance.GetPlugins("DbViewDriver.IDataSource");
			foreach (Object drv in drivers)
			{
				DbViewDriver.IDataSource driver = drv as DbViewDriver.IDataSource;
				if (driver.DriverName == ci.Driver) // match against value held with connection
				{
					// this.m_Source = driver;
					SetDriver(driver);

					// keep connection list manager informed of the change of driver.
					if (ConnectionList.CurrentDriverName != driver.DriverName)
						ConnectionList.CurrentDriverName = driver.DriverName;
				}
			}
			// Trouble.
			if (this.m_Source == null)
			{
				string message = string.Format("The connection {0} is missing driver details", ci.Name);
				if (ci.Driver.Length > 0)
					message = string.Format("Cannot find the driver {0} of connection {1}", ci.Driver, ci.Name);
				throw new DbViewApplicationException(message);
			}
			// and set the connection string
			this.m_Source.SetCurrentConnection(ci);
			// keep this for various purposes
			this.m_CurrentConnectionTag = ci.Name;
			// store as last used and save the info.

			// TODO: Change this when multi-tab happens
			DbView.Resources.ConnectionListMgr.Instance.SetLastUsedConnection(ci);
		}

		public void SaveConfig(bool OnEnd)
		{
			SaveConfig(OnEnd, 0);
		}

		// This is so complicate because it is trying to cope with the scenario
		// of multiple instances closing down almost simultaneously when the machine
		// As written this sounds like a bad design. Probably because the feature was
		// bolted on. 
		void SaveConfig(bool OnEnd, int tryNumber)
		{
			try
			{
				if (OnEnd)
					DbView.Resources.ConfigDoc.Instance.RemoveTempViews();
				DbView.Resources.ConfigDoc.Instance.SaveConfig(); // ie. use the filename used in the load
			}
			catch (IOException exc)
			{
				if (tryNumber < 3) // three goes
				{
					Random RandomNumber = new Random();
					int x = RandomNumber.Next(200); // 0-200ms delay
					System.Threading.Thread.Sleep(x);
					SaveConfig(OnEnd, ++tryNumber);
				}
				else
				{
					throw new Exception(String.Format("Could not save configuration file. Messag:\n{0}", exc.Message));
				}
			}
		}

		// Levergae the exposed location of the main ConfigDoc file supplied at
		// Load time to supply the path to other resources.
		// I guess ideally these should be managed in their own resource objects.
		private string FigureOutConfigPath()
		{
			string configFile = DbView.Resources.ConfigDoc.Instance.FileName;
			return Path.GetDirectoryName(configFile);
		}

		// Model state.
		// The only permissable state that the model must relate to 
		// a single grid display managed by a single presenter object.
		// all other data is supplied from central resources such as the ConfigDoc
		private string m_CurrentConnectionTag = "";			// one connection
		private DbViewDriver.IDataSource m_Source = null;	// configuring a data source
		//private string m_CurrentDriverName = "";			// of this type

	}
}
